import OwnListItem from "./OwnListItem";

const {
  ccclass,
  property
} = cc._decorator;

// 滚动方向
enum ScrollDirection {
  Vertical = 1,
    Horizontal,
}

interface GetListDataCount {
  /**
   * @return  获得列表数据总数
   */
  (): number;
}

interface GetItemType {
  /**
   * @description 获得item类型
   * @param dataIndex 数据索引
   * @return  item类型
   */
  (dataIndex ? : number): string;
}

interface GetItemSize {
  /**
   * @description 获得item尺寸
   * @param dataIndex  数据索引
   * @return  item尺寸,垂直时获取height,水平时获取widget
   */
  (dataIndex ? : number): number;
}

interface GetItem {
  /**
   * @description 获得item
   * @param dataIndex 数据索引
   * @param type  item类型
   * @return  item
   */
  (dataIndex ? : number, type ? : string): OwnListItem;
}

interface GetItemData {
  /**
   * @param dataIndex 数据索引
   * @return  获得item数据
   */
  (dataIndex ? : number): any;
}

interface InitParam {
  getListDataCount: GetListDataCount,
    getItemSize: GetItemSize,
    getItemType: GetItemType,
    getItem: GetItem,
    getItemData: GetItemData,
}

interface ItemPools {
  /**
   * @description 获得item类型对象池
   * @param index itemtype
   * @return  对象池
   */
  [index: string]: cc.NodePool;
}

@ccclass
export default class OwnListView extends cc.Component {
  @property({
    type: cc.Enum(ScrollDirection),
  })
  scrollDirection = ScrollDirection.Vertical;
  @property
  spacing: number = 0;
  @property
  topPadding: number = 0;
  @property
  bottomPadding: number = 0;
  @property
  padding: cc.Vec2 = cc.v2(0, 0);
  @property
  debug: boolean = false;

  private _scrollView: cc.ScrollView = null;
  private _content: cc.Node = null;
  private _delegate: InitParam = null;
  private _inited = false;

  private _scrollPosition = 0;
  private _activeItemIndexRange: cc.Vec2;
  private _itemPools: ItemPools = {};

  private _itemsOffset: Array < number > ; // bottom side of cell position
  private _itemsSize: Array < number > ;
  private _activeItems = new Array < OwnListItem > ();  // 可视item

  onLoad() {
    this._scrollView = this.node.getComponent(cc.ScrollView);
    if (!this._scrollView) {
      this._scrollView = this.node.addComponent(cc.ScrollView);
      this._scrollView.vertical = this.scrollDirection === ScrollDirection.Vertical;
      this._scrollView.horizontal = this.scrollDirection === ScrollDirection.Horizontal;
    }

    this._content = new cc.Node();
    this._content.setAnchorPoint(0, 1);
    this.node.addChild(this._content);
    this._scrollView.content = this._content;

    if (this.debug) this._content.addComponent(cc.Graphics);

    this._inited = true;
    if (this._delegate) this._load();
  }

  update() {
    if (this.debug) {
      const graphics = this._content.getComponent(cc.Graphics);
      graphics.clear();
      graphics.fillColor = cc.Color.YELLOW;
      graphics.fillRect(0, 0, this._content.width, this._content.height);
    }
  }

  onEnable() {
    this.node.on('scrolling', this._onScrolling, this);
  }

  onDisable() {
    this.node.targetOff(this);
  }

  // 外部接口
  /**
   * @param param 初始化参数
   */
  public init(param: InitParam) {
    let needClear = false;
    if (this._delegate) needClear = true;
    this._delegate = param;
    if (this._inited) {
      if (needClear) this._clear();
      this._load();
    }
  }

  /**
   * @description 重新刷新列表数据,会更新item尺寸
   * @param keepPos 保持当前坐标
   */
  public reload(keepPos: boolean = false) {
    this._clear(keepPos);
    this._load();
  }

  /**
   * @description 刷新可视区item数据,不会改变item尺寸
   */
  public refresh() {
    this._updateActiveItemData();
  }

  /**
   * @description 获取item在scroll中的相对坐标
   * @param index item索引
   * @return  item相对坐标
   */
  public getItemPosInScroll(index:number):cc.Vec2 {
		let sp = this._getItemPosByIndex(index);
		if (this.scrollDirection == ScrollDirection.Vertical) {
			return new cc.Vec2(0, sp);
		} else {
			return new cc.Vec2(sp * -1, 0);
		}
  }

  /**
   * @description 滚动到指定item
   * @param index item索引
   * @param timeInSecond 滚动时间
   * @param attenuated 惯性减速
   */
  public scrollToItem(index: number, timeInSecond:number=0.1, attenuated: boolean = true) {
		let pos = this.getItemPosInScroll(index);
		this._scrollView.scrollTo(pos, timeInSecond, attenuated);
  }

  // 内部处理
	private  _onScrolling() {
		if (!this._delegate) return;
		const offset = this._scrollView.getScrollOffset();
		if (this.scrollDirection == ScrollDirection.Vertical) {
			this._scrollPosition = offset.y;
		} else {
			this._scrollPosition = offset.x * -1;
		}

		// refresh active cell with new scroll position
		this._refreshActiveItems();
  }
  
  /**
   * @description 清空列表
   * @param keepPos 是否保持当前坐标
   */
  private _clear(keepPos: boolean = false) {
    if (this._activeItems) {
      while(this._activeItems.length > 0) {
        this._recycleItem(this._activeItems.length - 1);
      }
    }

    this._activeItemIndexRange = cc.v2(-1,-1);
    if (!keepPos) {
      this._scrollPosition = 0;
      this._content.x = 0;
      this._content.y = 0;
    }
  }

  /**
   * @description 加载列表
   */
	private _load() {
		const dataLen = this._delegate.getListDataCount();
		if (dataLen <= 0) return;

		let offset = this.topPadding;
		this._itemsOffset = new Array<number>(dataLen);
		this._itemsSize = new Array<number>(dataLen);
		for (let i = 0; i < dataLen; i++) {
			let s = this._delegate.getItemSize(i)
			this._itemsSize[i] = s;
			offset = s + (i == 0 ? 0 : this.spacing) + offset;
			this._itemsOffset[i] = offset;
		}
		offset += this.bottomPadding;

		if (this.scrollDirection == ScrollDirection.Vertical) {
			this._content.setContentSize(this.node.width, offset);
		} else {
			this._content.setContentSize(offset, this.node.height);
		}

		const range = this._getActiveItemIndexRange();
		this._activeItemIndexRange = range;

		for (let i = range.x; i <= range.y; i++) {
			this._addItem(i);
		}
  }
  
  /**
   *  @description  刷新可视item
   */
  private _refreshActiveItems() {
		// 更新scroll中可视item
		const range = this._getActiveItemIndexRange();
		// 检查是否有itme需要更新
		if (range.equals(this._activeItemIndexRange)) return;

		// 回收超出可视区域的item
		let i = 0;
		while (i < this._activeItems.length) {
			let cell = this._activeItems[i];
			if (cell.dataIndex < range.x || cell.dataIndex > range.y) {
				this._recycleItem(i);
			} else {
				i++;
			}
		}

		// 添加可显示item
		// !TODO: boost this part effecient
		for (let i = range.x; i <= range.y; i++) {
			let needadd = true;
			for (let j = 0; j < this._activeItems.length; j++) {
				if (this._activeItems[j].dataIndex == i) {
					needadd = false;
					break;
				}
			}

			if (needadd) this._addItem(i);
		}

    // 更新item区域
		this._activeItemIndexRange = range;
  }
  
  /**
   * @description 回收item
   * @param itemIndex item索引
   */
  private _recycleItem(index:number) {
		// !TODO: need store this cell in node pool
		const item = this._activeItems[index];
		this._activeItems.splice(index, 1);
		item.node.removeFromParent(false);
		item.dataIndex = -1;

		if (!this._itemPools[item.itemType]) {
			this._itemPools[item.itemType] = new cc.NodePool();
		}
		let pool = this._itemPools[item.itemType];
		pool.put(item.node);
  }
  
  /**
   * @description 从对象池中拿item
   * @param itemType 对象池类型
   */
  private _getItemFromPool(itemType:string): OwnListItem | null {
		if (!this._itemPools[itemType]) return null;
		let pool = this._itemPools[itemType];
		let cellNode = pool.get();
		if (!cellNode) return null;
		return cellNode.getComponent(OwnListItem);
  }
  
  /**
   * @description 获得可显示item区域
   */
  private _getActiveItemIndexRange():cc.Vec2 {
		let startPos = this._scrollPosition;
		let endPos = startPos + (this.scrollDirection == ScrollDirection.Vertical ? this.node.height : this.node.width);
		return new cc.Vec2(this._getItemIndexOfPos(startPos), this._getItemIndexOfPos(endPos));
  }
  
  /**
   * @description 获得item在可视坐标索引
   * @param pos 
   */
	private _getItemIndexOfPos(pos:number): number {
		// !TODO: boost this function speed by using binary search
		for (let i = 0; i < this._itemsOffset.length; i++) {
			if (this._itemsOffset[i] >= pos) return i;
		}
		return this._itemsOffset.length - 1;
  }

  	/**
	 * @description 获取item顶部坐标
	 * @param index item索引
	 */
	private _getItemPosByIndex(index:number): number {
		return this._itemsOffset[index] - this._itemsSize[index];
	}

  /**
   * @description 添加item
   * @param dataIndex 数据索引
   */
	private _addItem(dataIndex:number) {
		const id = this._delegate.getItemType(dataIndex);
		let item = this._getItemFromPool(id);
		if (!item) {
			item = this._delegate.getItem(dataIndex);
			item.node.setAnchorPoint(0, 1);
			item.itemType = id;
		}

		item.dataIndex = dataIndex;
		item.enabled = true;
		this._activeItems.push(item)
		this._content.addChild(item.node);
		if (this.scrollDirection == ScrollDirection.Vertical) {
			item.node.x = this.padding.x;
			item.node.y = (this._itemsOffset[item.dataIndex] - this._itemsSize[item.dataIndex]) * -1;
			item.node.setContentSize(this.node.width - this.padding.x - this.padding.y, this._itemsSize[dataIndex]);
		} else {
			item.node.x = (this._itemsOffset[item.dataIndex] - this._itemsSize[item.dataIndex]);
			item.node.y = this.padding.x * -1;
			item.node.setContentSize(this._itemsSize[dataIndex], this.node.height - this.padding.x - this.padding.y);
		}

		item.dataIndex = dataIndex;
		this._updateItemContent(item);
	}

  /**
   * @description 更新可视item数据
   */
	private _updateActiveItemData() {
		this._activeItems.forEach(item => {
			this._updateItemContent(item);
		});
	}

  /**
   * @description 更新item数据
   * @param item \
   */
	private _updateItemContent(item:OwnListItem) {
		let data = null
		if (this._delegate.getItemData) {
			data = this._delegate.getItemData(item.dataIndex);
		}

		item.updateItemData(data);
	}
}